/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.service.impl;

import com.alibaba.fastjson.JSON;
import com.bifrost.entity.BonusExceptionOrder;
import com.bifrost.entity.MerchantStore;
import com.bifrost.exception.BonusPointsNotEnoughException;
import com.bifrost.exception.OriginOrderNotFundException;
import com.bifrost.model.ExternalOrder;
import com.bifrost.model.ExternalOrderItem;
import com.bifrost.model.ExternalPayment;
import com.bifrost.model.ReProcessModel;
import com.bifrost.repository.BonusExceptionOrderRepository;
import com.bifrost.sellRepository.SelldetailCbRepository;
import com.bifrost.sellRepository.SellheadCbRepository;
import com.bifrost.sellRepository.SellpaymentCbRepository;
import com.bifrost.sellRepository.SendjfWxLogRepository;
import com.bifrost.sellentity.SellPaymentCb;
import com.bifrost.sellentity.SelldetailCb;
import com.bifrost.sellentity.Sellheadcb;
import com.bifrost.sellentity.SendjfWxLog;
import com.bifrost.service.BonusPointsService;
import com.bifrost.service.ExternalOrderService;
import com.bifrost.service.StoreService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * 处理外部订单
 *
 * @author joewee
 * @version 1.0.0
 * @Date 2018/5/11 09:02
 */
@Service
public class ExternalOrderServiceImpl implements ExternalOrderService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExternalOrderService.class);
    /**
     * 注该map线程不安全，仅做缓存使用
     */
    private final Map<String, MerchantStore> storeCacheMap = new HashMap();
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    BonusPointsService bonusPointsService;
    @Autowired
    StoreService storeService;
    @Value("${running.mode:#{null}}")
    private String runningMode;
    @Autowired
    BonusExceptionOrderRepository exceptionOrderRepository;


    /**
     * 中间表
     */
    @Autowired
    SendjfWxLogRepository sendjfWxLogRepository;

    /**
     * 消费明细
     */
    @Autowired
    SelldetailCbRepository selldetailCbRepository;

    /**
     * 小票头信息
     */
    @Autowired
    SellheadCbRepository sellheadCbRepository;

    /**
     * 支付明细
     */
    @Autowired
    SellpaymentCbRepository sellpaymentCbRepository;

    /**
     * 处理外部订单
     */
    @Override
    public void proces() {
        try {
            List<SendjfWxLog> list = sendjfWxLogRepository.findByPage();
            while (CollectionUtils.isNotEmpty(list)) {
                int size = list.size();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("该批次数量共{}条数据", size);
                }
                for (int i = 0; i < size; i++) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("处理第{}/{}条数据", i + 1,size);
                    }
                    SendjfWxLog sendjfWxLog = list.get(i);
                    try {
                        //小票序号
                        Integer tbillno = sendjfWxLog.getTbillno();
                        // 小票头信息
                        Sellheadcb sellhead = sellheadCbRepository.findByShbillno(tbillno);
                        // 门店
                        String shmarket = sellhead.getShmarket().replace("A", "1").replace("B", "2").replace("C", "3")
                                .replace("D", "6");
                        String storeCode = shmarket;
                        //综合物理体转百货
                        if ("8".equals(StringUtils.substring(shmarket, 1, 2))) {
                            storeCode = "1" + StringUtils.substring(shmarket, 1);
                        }
                        if (!storeIsAvailable(storeCode)) {
                            LOGGER.debug("门店{}在系统中不可用该次处理将被忽略", storeCode);
                            sendjfWxLogRepository.delete(tbillno);
                            continue;
                        }
                        // 小票号
                        String shinvno = sellhead.getShinvno();
                        // 单据类别 (1销售 ， 4退货， n批发销售， q批发退货，P预销售，S预销售退货.)
                        String shdjlb = sellhead.getShdjlb();
                        // 会员卡号
                        String shcustno = sellhead.getShcustno();
                        // 交易日期
                        Date shdate = sellhead.getShdate();
                        // 收银机号
                        String shsyjid = sellhead.getShsyjid();
                        if (StringUtils.isAnyBlank(shinvno, shmarket, shdjlb, shcustno)||StringUtils.startsWithIgnoreCase(shsyjid,"X")) {
                            if (LOGGER.isWarnEnabled()) {
                                LOGGER.warn("门店{}小票号为{}会员{}已被忽略", shmarket, shinvno,shcustno);
                            }
                            sendjfWxLogRepository.delete(tbillno);
                            continue;
                        }
                        /**
                         *
                         * 支付详情-----------------------------------------start---------------------------
                         * **/
                        List<ExternalPayment> payments = new ArrayList<>();
                        List<SellPaymentCb> SellPaymentCblist = sellpaymentCbRepository.findAllBySpbillno(tbillno);
                        for (SellPaymentCb sellPaymentCb : SellPaymentCblist) {
                            // 原始单据号
                            String spno = sellPaymentCb.getSpno();
                            // 订单金额
                            BigDecimal spmoney = sellPaymentCb.getSpmoney();
                            ExternalPayment externalPayment = new ExternalPayment();
                            externalPayment.setAmount(spmoney);
                            externalPayment.setTransactionNumber(spno);
                            payments.add(externalPayment);
                        }
                        /**
                         *
                         * 商品详情-----------------------------------------start---------------------------
                         * **/
                        List<SelldetailCb> selldetailCblist = selldetailCbRepository.findAllBySdbillno(tbillno);
                        List<ExternalOrder> orders = new ArrayList<>(StringUtils.equalsAnyIgnoreCase(shdjlb, "4", "q", "s") ? selldetailCblist.size() : 1);
                        if (CollectionUtils.isNotEmpty(selldetailCblist)) {
                            Map<String, ExternalOrder> tmp = new HashMap();
                            detail:
                            for (SelldetailCb selldetailCb : selldetailCblist) {
                                List<ExternalOrderItem> orderItems = null;
                                /**
                                 * 退货
                                 * */
                                if (StringUtils.equalsAnyIgnoreCase(shdjlb, "4", "q", "s")) {
                                    //原小票号
                                    Integer sdyinvno = selldetailCb.getSdyinvno();
                                    //原收银机号
                                    String sdysyjid = selldetailCb.getSdysyjid();
                                    if (StringUtils.isAnyBlank(sdysyjid)||sdyinvno==null) {
                                        LOGGER.warn("小票号{}异常:原小票号{}收银机号{}",tbillno,sdyinvno,sdysyjid);
                                        sendjfWxLogRepository.delete(tbillno);
                                        break detail;
                                    }
                                    String key = sdysyjid + sdyinvno;
                                    if (!tmp.containsKey(key)) {
                                        ExternalOrder externalOrder = new ExternalOrder();
                                        // 小票号 (门店号+收银机号+小票号)
                                        String orderNumber = String.format("%s%s%s%s", shmarket, shsyjid, shinvno, orders.size() == 0 ? "" : ("_" + orders.size()));
                                        externalOrder.setType(ExternalOrder.Types.REFUND.toString());
                                        List<ExternalOrderItem> items = new ArrayList<ExternalOrderItem>(selldetailCblist.size());
                                        externalOrder.setItems(items);
                                        externalOrder.setPayments(payments);
                                        externalOrder.setOrderNumber(orderNumber);
                                        externalOrder.setCardNumber(shcustno);
                                        //原小票号(门店号+收银机号+小票号)
                                        externalOrder.setOriginNumber(String.format("%s%s%s", shmarket, sdysyjid, sdyinvno));
                                        externalOrder.setStoreCode(storeCode);
                                        externalOrder.setBusinessDate(shdate);
                                        tmp.put(key, externalOrder);
                                        orders.add(externalOrder);
                                    }
                                    orderItems = tmp.get(key).getItems();
                                } else {
                                    //销售
                                    if (!tmp.containsKey(tbillno.toString())) {
                                        ExternalOrder externalOrder = new ExternalOrder();
                                        externalOrder.setType(ExternalOrder.Types.SALES.toString());
                                        List<ExternalOrderItem> items = new ArrayList<ExternalOrderItem>(selldetailCblist.size());
                                        externalOrder.setItems(items);
                                        externalOrder.setPayments(payments);
                                        //小票号
                                        externalOrder.setOrderNumber(String.format("%s%s%s", shmarket, shsyjid, shinvno));
                                        externalOrder.setCardNumber(shcustno);
                                        externalOrder.setOriginNumber(null);
                                        externalOrder.setStoreCode(storeCode);
                                        externalOrder.setBusinessDate(shdate);
                                        tmp.put(tbillno.toString(), externalOrder);
                                        orders.add(externalOrder);
                                    }
                                    orderItems = tmp.get(tbillno.toString()).getItems();
                                }
                                // 商品代码
                                String sdgdid = selldetailCb.getSdgdid();
                                // 商品类别
                                String sdcatid = selldetailCb.getSdcatid();
                                // 售价
                                BigDecimal sdsj = selldetailCb.getSdsj();
                                // 数量
                                BigDecimal sdsl = selldetailCb.getSdsl();
                                // 实际金额
                                //BigDecimal sdsjje = selldetailCb.getSdsjje();
                                // 成交金额
                                BigDecimal sdcjje = selldetailCb.getSdcjje();

                                ExternalOrderItem item = new ExternalOrderItem();
                                item.setItemCode(sdgdid);
                                item.setCategoryCode(sdcatid);
                                item.setQuantity(sdsl);
                                item.setSalesPrice(sdsj);
                                item.setRetailPrice(sdsj);
                                item.setSubtotal(sdcjje);
                                orderItems.add(item);
                            }
                        }
                        if (CollectionUtils.isNotEmpty(orders)) {
                            //调用内部事务方法
                            try{
                                ((ExternalOrderService) AopContext.currentProxy()).excute(orders);
                                sendjfWxLogRepository.delete(sendjfWxLog);
                                if (LOGGER.isDebugEnabled()) {
                                    LOGGER.debug("处理第{}/{}条数据成功", i + 1,size);
                                }
                            }catch (Exception e){
                                /*
                                * 记录异常订单
                                * */
                                orders.forEach(externalOrder -> {
                                    BonusExceptionOrder exceptionOrder =exceptionOrderRepository.findFirstByCardNumberAndNumber(externalOrder.getCardNumber(),externalOrder.getOrderNumber());
                                    if(exceptionOrder==null){
                                        exceptionOrder= new BonusExceptionOrder();
                                        exceptionOrder.setCardNumber(externalOrder.getCardNumber());
                                        exceptionOrder.setData(externalOrder.toString());
                                        exceptionOrder.setNumber(externalOrder.getOrderNumber());
                                        exceptionOrder.setStoreCode(externalOrder.getStoreCode());
                                        exceptionOrder.setOriginNumber(externalOrder.getOriginNumber());
                                        exceptionOrder.setRemark(e.getMessage());
                                        exceptionOrderRepository.save(exceptionOrder);
                                    }
                                });
                                sendjfWxLogRepository.delete(sendjfWxLog);
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.error(e.getMessage());
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("处理第{}/{}条数据失败", i + 1,size);
                        }
                        sendjfWxLogRepository.delete(sendjfWxLog);
                        continue;
                    }
                }
                list = sendjfWxLogRepository.findByPage();
            }
        } catch (Exception e) {
            LOGGER.error("获取数据失败" + e.getMessage());
        }
    }

    /**
     * 重新处理外部订单预处理
     *
     */
    @Override
    public void reProces(String market,Date begin,Date end) {
        if (StringUtils.isNotBlank(market)) {
            Assert.notNull(begin,"开始时间不能为空");
            Assert.notNull(end,"结束时间不能为空");
            List<Sellheadcb> sellheadcbs = sellheadCbRepository.findByShmarketAndShdateBetween(market,begin,end);
               for (Sellheadcb head : sellheadcbs){
                   try {
                       pretreatment(head);
                       try {
                           sendjfWxLogRepository.delete(head.getShbillno());
                       }catch (Exception e){
                           if (LOGGER.isInfoEnabled()) {
                               LOGGER.info("小票{}Log表中不存在：{}", head.getShbillno(),e.getMessage());
                           }
                       }
                   }catch (Exception e){
                       LOGGER.error(e.getMessage(),e);
                   }
               }
        }
    }

    @Override
    public void reProces(String json) {
        try {
            ReProcessModel model = JSON.parseObject(json,ReProcessModel.class);
            reProces(model.getMarket(),model.getBegin(),model.getEnd());
        }catch (Exception e){
            LOGGER.error(e.getMessage(),e);
        }
    }

    private void pretreatment(Sellheadcb sellhead){
        //小票序号
        Integer tbillno = sellhead.getShbillno();
        // 门店
        String shmarket = sellhead.getShmarket().replace("A", "1").replace("B", "2").replace("C", "3")
                .replace("D", "6");
        String storeCode = shmarket;
        //综合物理体转百货
        if ("8".equals(StringUtils.substring(shmarket, 1, 2))) {
            storeCode = "1" + StringUtils.substring(shmarket, 1);
        }
        if (!storeIsAvailable(storeCode)) {
            LOGGER.debug("门店{}在系统中不可用该次处理将被忽略", storeCode);
            return;
        }
        // 小票号
        String shinvno = sellhead.getShinvno();
        // 单据类别 (1销售 ， 4退货， n批发销售， q批发退货，P预销售，S预销售退货.)
        String shdjlb = sellhead.getShdjlb();
        // 会员卡号
        String shcustno = sellhead.getShcustno();
        // 交易日期
        Date shdate = sellhead.getShdate();
        // 收银机号
        String shsyjid = sellhead.getShsyjid();
        if (StringUtils.isAnyBlank(shinvno, shmarket, shdjlb, shcustno)) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("门店{}小票号为{}会员{}已被忽略", shmarket, shinvno,shcustno);
            }
            return;
        }
        /**
         *
         * 支付详情-----------------------------------------start---------------------------
         * **/
        List<ExternalPayment> payments = new ArrayList<>();
        List<SellPaymentCb> SellPaymentCblist = sellpaymentCbRepository.findAllBySpbillno(tbillno);
        for (SellPaymentCb sellPaymentCb : SellPaymentCblist) {
            // 原始单据号
            String spno = sellPaymentCb.getSpno();
            // 订单金额
            BigDecimal spmoney = sellPaymentCb.getSpmoney();
            ExternalPayment externalPayment = new ExternalPayment();
            externalPayment.setTransactionNumber(spno);
            externalPayment.setAmount(spmoney);
            payments.add(externalPayment);
        }
        /**
         *
         * 商品详情-----------------------------------------start---------------------------
         * **/
        List<SelldetailCb> selldetailCblist = selldetailCbRepository.findAllBySdbillno(tbillno);
        List<ExternalOrder> orders = new ArrayList<>(StringUtils.equalsAnyIgnoreCase(shdjlb, "4", "q", "s") ? selldetailCblist.size() : 1);
        if (CollectionUtils.isNotEmpty(selldetailCblist)) {
            Map<String, ExternalOrder> tmp = new HashMap();
            detail:
            for (SelldetailCb selldetailCb : selldetailCblist) {
                List<ExternalOrderItem> orderItems = null;
                /**
                 * 退货
                 * */
                if (StringUtils.equalsAnyIgnoreCase(shdjlb, "4", "q", "s")) {
                    //原小票号
                    Integer sdyinvno = selldetailCb.getSdyinvno();
                    //原收银机号
                    String sdysyjid = selldetailCb.getSdysyjid();
                    if (StringUtils.isAnyBlank(sdysyjid)||sdyinvno==null) {
                        LOGGER.warn("小票号{}异常:原小票号{}收银机号{}",tbillno,sdyinvno,sdysyjid);
                        break detail;
                    }
                    String key = sdysyjid + sdyinvno;
                    if (!tmp.containsKey(key)) {
                        ExternalOrder externalOrder = new ExternalOrder();
                        // 小票号 (门店号+收银机号+小票号)
                        String orderNumber = String.format("%s%s%s%s", shmarket, shsyjid, shinvno, orders.size() == 0 ? "" : ("_" + orders.size()));
                        externalOrder.setType(ExternalOrder.Types.REFUND.toString());
                        List<ExternalOrderItem> items = new ArrayList<ExternalOrderItem>(selldetailCblist.size());
                        externalOrder.setItems(items);
                        externalOrder.setPayments(payments);
                        externalOrder.setCardNumber(shcustno);
                        externalOrder.setOrderNumber(orderNumber);
                        //原小票号(门店号+收银机号+小票号)
                        externalOrder.setOriginNumber(String.format("%s%s%s", shmarket, sdysyjid, sdyinvno));
                        externalOrder.setStoreCode(storeCode);
                        externalOrder.setBusinessDate(shdate);
                        tmp.put(key, externalOrder);
                        orders.add(externalOrder);
                    }
                    orderItems = tmp.get(key).getItems();
                } else {
                    //销售
                    if (!tmp.containsKey(tbillno.toString())) {
                        ExternalOrder externalOrder = new ExternalOrder();
                        externalOrder.setType(ExternalOrder.Types.SALES.toString());
                        List<ExternalOrderItem> items = new ArrayList<ExternalOrderItem>(selldetailCblist.size());
                        externalOrder.setItems(items);
                        externalOrder.setPayments(payments);
                        //小票号
                        externalOrder.setOrderNumber(String.format("%s%s%s", shmarket, shsyjid, shinvno));
                        externalOrder.setCardNumber(shcustno);
                        externalOrder.setOriginNumber(null);
                        externalOrder.setBusinessDate(shdate);
                        externalOrder.setStoreCode(storeCode);
                        tmp.put(tbillno.toString(), externalOrder);
                        orders.add(externalOrder);
                    }
                    orderItems = tmp.get(tbillno.toString()).getItems();
                }
                // 商品代码
                String sdgdid = selldetailCb.getSdgdid();
                // 商品类别
                String sdcatid = selldetailCb.getSdcatid();
                // 售价
                BigDecimal sdsj = selldetailCb.getSdsj();
                // 数量
                BigDecimal sdsl = selldetailCb.getSdsl();
                // 实际金额
                //BigDecimal sdsjje = selldetailCb.getSdsjje();
                // 成交金额
                BigDecimal sdcjje = selldetailCb.getSdcjje();

                ExternalOrderItem item = new ExternalOrderItem();
                item.setItemCode(sdgdid);
                item.setCategoryCode(sdcatid);
                item.setQuantity(sdsl);
                item.setRetailPrice(sdsj);
                item.setSubtotal(sdcjje);
                item.setSalesPrice(sdsj);
                orderItems.add(item);
            }
        }
        if (CollectionUtils.isNotEmpty(orders)) {
            //调用内部事务方法
            try{
                ((ExternalOrderService) AopContext.currentProxy()).excute(orders);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("处理小票{}成功", tbillno);
                }
            }catch (Exception e){
                                /*
                                * 记录异常订单
                                * */
                orders.forEach(externalOrder -> {
                    BonusExceptionOrder exceptionOrder =exceptionOrderRepository.findFirstByCardNumberAndNumber(externalOrder.getCardNumber(),externalOrder.getOrderNumber());
                    if(exceptionOrder==null){
                        exceptionOrder= new BonusExceptionOrder();
                        exceptionOrder.setCardNumber(externalOrder.getCardNumber());
                        exceptionOrder.setData(externalOrder.toString());
                        exceptionOrder.setNumber(externalOrder.getOrderNumber());
                        exceptionOrder.setOriginNumber(externalOrder.getOriginNumber());
                        exceptionOrder.setStoreCode(externalOrder.getStoreCode());
                        exceptionOrder.setRemark(e.getMessage());
                        exceptionOrderRepository.save(exceptionOrder);
                    }
                });
            }
            return;
        }
    }
    /**
     * 执行调用接口
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void excute(List<ExternalOrder> orders) throws BonusPointsNotEnoughException, OriginOrderNotFundException {
        Assert.notNull(orders, "订单不能为空");
        // 调用接口处理外部订单积分
        for (ExternalOrder order : orders) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("开始处理外部订单{}", order.getOrderNumber());
            }
            Long processExternalOrder = bonusPointsService.processExternalOrder(order);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("外部订单{}处理成功，流水号{}", order.getOrderNumber(), processExternalOrder);
            }
        }
    }

    private boolean storeIsAvailable(String externalId) {
        MerchantStore store = storeCacheMap.get(externalId);
        if (store == null) {
            //TODO 缓存门店信息减少数据库查询
            synchronized (storeCacheMap) {
                store = storeCacheMap.get(externalId);
                if (store == null) {
                    store = storeService.getMerchantStoreByExternalId(externalId);
                    if (store != null) {
                        storeCacheMap.put(externalId, store);
                    }
                }
            }
        }
        return store != null && Boolean.TRUE.equals(store.getAvailable());
    }
}
